package com.macro.mall.portal.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.macro.mall.common.exception.Asserts;
import com.macro.mall.common.util.RedisUtils;
import com.macro.mall.mapper.UmsMemberLevelMapper;
import com.macro.mall.mapper.UmsMemberMapper;
import com.macro.mall.model.UmsMember;
import com.macro.mall.model.UmsMemberExample;
import com.macro.mall.model.UmsMemberLevel;
import com.macro.mall.model.UmsMemberLevelExample;
import com.macro.mall.portal.domain.LoginDTO;
import com.macro.mall.portal.domain.LoginResponse;
import com.macro.mall.portal.domain.MemberDetails;
import com.macro.mall.portal.domain.UmsMemberVO;
import com.macro.mall.portal.modules.xx.mapper.DmUserWechatMapper;
import com.macro.mall.portal.modules.xx.pojo.entity.DmUserWechat;
import com.macro.mall.portal.service.UmsMemberCacheService;
import com.macro.mall.portal.service.UmsMemberService;
import com.macro.mall.security.component.BaseUserDetails;
import com.macro.mall.security.util.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 会员管理Service实现类
 * Created by macro on 2018/8/3.
 */
@Slf4j
@Service
public class UmsMemberServiceImpl implements UmsMemberService {
    private static final Logger LOGGER = LoggerFactory.getLogger(UmsMemberServiceImpl.class);
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private UmsMemberMapper memberMapper;
    @Autowired
    private UmsMemberLevelMapper memberLevelMapper;
    @Autowired
    private UmsMemberCacheService memberCacheService;
    @Autowired
    private WxMaService wxMaService;
    @Autowired
    private DmUserWechatMapper dmUserWechatMapper;
    @Value("${redis.key.authCode}")
    private String REDIS_KEY_PREFIX_AUTH_CODE;
    @Value("${redis.expire.authCode}")
    private Long AUTH_CODE_EXPIRE_SECONDS;
    @Autowired
    private UmsMemberMapper umsMemberMapper;

    @Override
    public UmsMember getByUsername(String username) {
        UmsMemberExample example = new UmsMemberExample();
        example.createCriteria().andUsernameEqualTo(username);
        List<UmsMember> memberList = memberMapper.selectByExample(example);
        if (!CollectionUtils.isEmpty(memberList)) {
            UmsMember member = memberList.get(0);
            return member;
        }
        return null;
    }

    @Override
    public UmsMember getById(Long id) {
        return memberMapper.selectByPrimaryKey(id);
    }


    public UmsMemberVO qryAndSave(String userName) {
        UmsMember umsMember = umsMemberMapper.selectByUserName(userName);
        if (umsMember == null) {
            umsMember = new UmsMember();
            umsMember.setUsername(userName);
            umsMember.setCreateTime(new Date());
            umsMemberMapper.insert(umsMember);
            umsMember = umsMemberMapper.selectByUserName(userName);
        }
        UmsMemberVO umsMemberVO = new UmsMemberVO();
        BeanUtils.copyProperties(umsMember, umsMemberVO);
        return umsMemberVO;
    }

    @Override
    public void register(String username, String password, String telephone, String authCode) {
        //验证验证码
        if (!verifyAuthCode(authCode, telephone)) {
            Asserts.fail("验证码错误");
        }
        //查询是否已有该用户
        UmsMemberExample example = new UmsMemberExample();
        example.createCriteria().andUsernameEqualTo(username);
        example.or(example.createCriteria().andPhoneEqualTo(telephone));
        List<UmsMember> umsMembers = memberMapper.selectByExample(example);
        if (!CollectionUtils.isEmpty(umsMembers)) {
            Asserts.fail("该用户已经存在");
        }
        //没有该用户进行添加操作
        UmsMember umsMember = new UmsMember();
        umsMember.setUsername(username);
        umsMember.setPhone(telephone);
        umsMember.setPassword(passwordEncoder.encode(password));
        umsMember.setCreateTime(new Date());
        umsMember.setStatus(1);
        //获取默认会员等级并设置
        UmsMemberLevelExample levelExample = new UmsMemberLevelExample();
        levelExample.createCriteria().andDefaultStatusEqualTo(1);
        List<UmsMemberLevel> memberLevelList = memberLevelMapper.selectByExample(levelExample);
        if (!CollectionUtils.isEmpty(memberLevelList)) {
            umsMember.setMemberLevelId(memberLevelList.get(0).getId());
        }
        memberMapper.insert(umsMember);
        umsMember.setPassword(null);
    }

    @Override
    public String generateAuthCode(String telephone) {
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            sb.append(random.nextInt(10));
        }
        memberCacheService.setAuthCode(telephone, sb.toString());
        return sb.toString();
    }

    @Override
    public void updatePassword(String telephone, String password, String authCode) {
        UmsMemberExample example = new UmsMemberExample();
        example.createCriteria().andPhoneEqualTo(telephone);
        List<UmsMember> memberList = memberMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(memberList)) {
            Asserts.fail("该账号不存在");
        }
        //验证验证码
        if (!verifyAuthCode(authCode, telephone)) {
            Asserts.fail("验证码错误");
        }
        UmsMember umsMember = memberList.get(0);
        umsMember.setPassword(passwordEncoder.encode(password));
        memberMapper.updateByPrimaryKeySelective(umsMember);
        memberCacheService.delMember(umsMember.getId());
    }


    @Override
    public Long getCurrentMemberId() {
        SecurityContext ctx = SecurityContextHolder.getContext();
        Authentication auth = ctx.getAuthentication();
        BaseUserDetails memberDetails = (BaseUserDetails) auth.getPrincipal();
        return memberDetails.getId();
    }

    @Override
    public void updateIntegration(Long id, Integer integration) {
        UmsMember record = new UmsMember();
        record.setId(id);
        record.setIntegration(integration);
        memberMapper.updateByPrimaryKeySelective(record);
        memberCacheService.delMember(id);
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        UmsMember member = getByUsername(username);
        if (member != null) {
            UmsMemberVO voByUmsMember = getVoByUmsMember(member);
            return new MemberDetails(voByUmsMember);
        }
        throw new UsernameNotFoundException("用户名或密码错误");
    }

    private UmsMemberVO getVoByUmsMember(UmsMember umsMember) {
        UmsMemberVO umsMemberVO = new UmsMemberVO();
        BeanUtils.copyProperties(umsMember, umsMemberVO);
        return umsMemberVO;
    }

    @Override
    public LoginResponse login(String username, String password) {
        //密码需要客户端加密后传递
        UserDetails userDetails = loadUserByUsername(username);
        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("密码不正确");
        }
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = jwtTokenUtil.generateToken(userDetails);
        LoginResponse loginResponse = new LoginResponse();
        loginResponse.setToken(token);
        return loginResponse;
    }


    @Override
    public LoginResponse wxLogin(LoginDTO loginDTO) throws Exception {
        LoginResponse loginResponse = new LoginResponse();
        try {
            String appid = loginDTO.getAppid();
            String code = loginDTO.getCode();

            //密码需要客户端加密后传递
            if (!wxMaService.switchover(appid)) {
                throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置，请核实！", appid));
            }

            String reqOpenId = loginDTO.getOpenid();
            if (StrUtil.isBlank(reqOpenId)) {
                WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
                String openid = session.getOpenid();
                String unionid = session.getUnionid();
                String sessionKey = session.getSessionKey();
                DmUserWechat dmUserWechat = dmUserWechatMapper.selectOne(Wrappers.<DmUserWechat>lambdaQuery().eq(DmUserWechat::getOpenId, openid));

                if (dmUserWechat == null) {
                    redisUtils.hset(openid, "sessionKey", sessionKey);
                    redisUtils.hset(openid, "unionId", unionid);
                    redisUtils.expireTimeUnit(openid, 5, TimeUnit.HOURS);
                    loginResponse.setOpenId(openid);
                } else {
                    Long dmId = dmUserWechat.getDmId();
                    UmsMember member = getById(dmId);
                    UmsMemberVO voByUmsMember = getVoByUmsMember(member);
                    MemberDetails userDetails = new MemberDetails(voByUmsMember);
                    String token = jwtTokenUtil.generateToken(userDetails);
                    loginResponse.setToken(token);
                }
            } else {
                String username = loginDTO.getMobile();
                //String sessionKey = (String) redisUtils.hget(reqOpenId, "sessionKey");
                String unionId = (String) redisUtils.hget(reqOpenId, "unionId");
                if (StrUtil.isBlank(unionId)) {
                    throw new RuntimeException("授权失效");
                }
                UmsMemberVO umsMember = this.qryAndSave(username);
                Long id = umsMember.getId();
                DmUserWechat dmUserWechat = new DmUserWechat();
                dmUserWechat.setOpenId(reqOpenId);
                dmUserWechat.setUnionId(unionId);
                dmUserWechat.setDmId(id);
                dmUserWechatMapper.insert(dmUserWechat);
                MemberDetails userDetails = new MemberDetails(umsMember);
                String token = jwtTokenUtil.generateToken(userDetails);
                loginResponse.setToken(token);
            }
        } finally {
            WxMaConfigHolder.remove();//清理ThreadLocal
        }
        return loginResponse;
    }

    @Override
    public String refreshToken(String token) {
        return jwtTokenUtil.refreshHeadToken(token);
    }

    //对输入的验证码进行校验
    private boolean verifyAuthCode(String authCode, String telephone) {
        if (StrUtil.isEmpty(authCode)) {
            return false;
        }
        String realAuthCode = memberCacheService.getAuthCode(telephone);
        return authCode.equals(realAuthCode);
    }

}
